// Check $redact pipeline stage.
load('jstests/aggregation/extras/utils.js');

t = db.jstests_aggregation_redact;
t.drop();

// this document will always be present but its content will change
t.save({
    _id: 1,
    level: 1,
    // b will present on level 3, 4, and 5
    b: {
        level: 3,
        c: 5,  // always included when b is included
        // the contents of d test that if we cannot see a document then we cannot see its
        // array-nested subdocument even if we have permissions to see the subdocument.
        // it also tests arrays containing documents we cannot see
        d: [
            {level: 1, e: 4},
            {f: 6},
            {level: 5, g: 9},
            "NOT AN OBJECT!!11!",  // always included when b is included
            [2, 3, 4, {level: 1, r: 11}, {level: 5, s: 99}]
            // nested array should always be included once b is
            // but the second object should only show up at level 5
        ]
    },
    // the contents of h test that in order to see a subdocument (j) we must be able to see all
    // parent documents (h and i) even if we have permissions to see the subdocument
    h: {level: 2, i: {level: 4, j: {level: 1, k: 8}}},
    // l checks that we get an empty document when we can see a document but none of its fields
    l: {m: {level: 3, n: 12}},
    // o checks that we get an empty array when we can see a array but none of its entries
    o: [{level: 5, p: 19}],
    // q is a basic field check and should always be included
    q: 14
});

// this document will sometimes be missing
t.save({
    _id: 2,
    level: 4,
});

a1 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 1]}, "$$DESCEND", "$$PRUNE"]}});
a2 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 2]}, "$$DESCEND", "$$PRUNE"]}});
a3 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 3]}, "$$DESCEND", "$$PRUNE"]}});
a4 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 4]}, "$$DESCEND", "$$PRUNE"]}});
a5 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 5]}, "$$DESCEND", "$$PRUNE"]}});

a1result = [{_id: 1, level: 1, l: {}, o: [], q: 14}];

a2result = [{
    _id: 1,
    level: 1,
    h: {
        level: 2,
    },
    l: {},
    o: [],
    q: 14
}];

a3result = [{
    _id: 1,
    level: 1,
    b: {
        level: 3,
        c: 5,
        d: [{level: 1, e: 4}, {f: 6}, "NOT AN OBJECT!!11!", [2, 3, 4, {level: 1, r: 11}]]
    },
    h: {
        level: 2,
    },
    l: {m: {level: 3, n: 12}},
    o: [],
    q: 14
}];

a4result = [
    {
      _id: 1,
      level: 1,
      b: {
          level: 3,
          c: 5,
          d: [{level: 1, e: 4}, {f: 6}, "NOT AN OBJECT!!11!", [2, 3, 4, {level: 1, r: 11}]]
      },
      h: {level: 2, i: {level: 4, j: {level: 1, k: 8}}},
      l: {m: {level: 3, n: 12}},
      o: [],
      q: 14
    },
    {
      _id: 2,
      level: 4,
    }
];

a5result = [
    {
      _id: 1,
      level: 1,
      b: {
          level: 3,
          c: 5,
          d: [
              {level: 1, e: 4},
              {f: 6},
              {level: 5, g: 9},
              "NOT AN OBJECT!!11!",
              [2, 3, 4, {level: 1, r: 11}, {level: 5, s: 99}]
          ]
      },
      h: {level: 2, i: {level: 4, j: {level: 1, k: 8}}},
      l: {m: {level: 3, n: 12}},
      o: [{level: 5, p: 19}],
      q: 14
    },
    {
      _id: 2,
      level: 4,
    }
];

assert.eq(a1.toArray(), a1result);
assert.eq(a2.toArray(), a2result);
assert.eq(a3.toArray(), a3result);
assert.eq(a4.toArray(), a4result);
assert.eq(a5.toArray(), a5result);

// Test redacts that are just a variable access (this can happen as a result of optimizations)
assert.eq(t.aggregate({$redact: "$$PRUNE"}).toArray(), []);
assert.eq(t.aggregate({$redact: "$$KEEP"}).toArray(), t.find().toArray());
assert.eq(t.aggregate({$redact: "$$DESCEND"}).toArray(), t.find().toArray());

// test $$KEEP
t.drop();
// entire document should be present at 2 and beyond
t.save({_id: 1, level: 2, b: {level: 3, c: 2}, d: {level: 1, e: 8}, f: 9});

b1 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 1]}, "$$KEEP", "$$PRUNE"]}});
b2 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 2]}, "$$KEEP", "$$PRUNE"]}});
b3 = t.aggregate({$redact: {$cond: [{$lte: ['$level', 3]}, "$$KEEP", "$$PRUNE"]}});

b1result = [];

b23result = [{_id: 1, level: 2, b: {level: 3, c: 2}, d: {level: 1, e: 8}, f: 9}];

assert.eq(b1.toArray(), b1result);
assert.eq(b2.toArray(), b23result);
assert.eq(b3.toArray(), b23result);
